#include "head.h"

list_t *head_ = NULL;
int sock__ = 0;
void handler(int sig) // ctrl+c关闭服务器时，回收空间关闭套接字
{
    list_free_(&head_); // 释放空间
    close(sock__);      // 关闭通信套接字
    printf("退出服务器成功\n");
    // printf("sig=%d\n",sig);
    exit(0); // 退出整个程序
}

void *fun_pthread(void *sockc) // 线程执行
{
    int sockfd_2 = *(int *)sockc;
    user ret;
    while (1)
    {
        ret = enter(sockfd_2);
        if (ret.dl == 0) // enter返回值为0的时候登录成功
        {
            while (1)
            {
                if (9 == function(sockfd_2, ret.id)) // 当客户端选择退出程序，通信套接字断开，返回9退出服务器
                    break;
            }
        }
        if (ret.dl == 5) // 用户选择退出程序选项时，返回值为5的时候，客户端断开连接，随之服务器关闭
        {
            break;
        }
    }
    close(sockfd_2); // 关闭这个套接字
    printf("线程结束\n");
    pthread_exit(NULL);
}

/*多路复用监听套接字变化*/
void my_select(int sockfd_1)
{
    struct timeval timeout = {1, 0};

    fd_set set1, set2; // 1.声明文件描述符集合
    FD_ZERO(&set1);    // 2.初始化文件描述符
    FD_ZERO(&set2);
    FD_SET(sockfd_1, &set2); // 3.将监听套接字加入集合

    list_t *head = NULL;        // 4.创建一个列表指针
    list_add_(&head, sockfd_1); // 5.创建列表并把监听套接字放在列表数据（而且在列表头部）
    head_ = head;               // 将列表头保存下来以便回收空间
    signal(SIGINT, handler);
    while (1)
    {
        set1 = set2;
        int n = select(getmax(&head) + 1, &set1, NULL, NULL, &timeout);
        if (n < 0) // 小于零说明函数select出现错误
        {
            printf("select函数执行出错\n");
            break;
        }
        if (n == 0) // 等于零说明监听超时
        {
            continue; // 检测超时
        }
        if (n > 0) // 监听到了监听套接字or通信套接字变化
        {
            if (FD_ISSET(sockfd_1, &set1)) // 发生状态变化的是监听套接字
            {
                int sockc = connect_1(sockfd_1); // 如果是就用监听套接字返回 通信套接字
                FD_SET(sockc, &set2);            // 把通信套接字加入描述符集
                list_add_(&head, sockc);         // 把得到的通信套接字加入列表
                const char *p = "欢迎访问服务器";
                send(sockc, p, strlen(p), 0); // 并向相应连接的客户端发送：“欢迎访问服务器”
            }
            else
            {
                list_t *p = head->next;
                while (p != NULL)
                {
                    int sockc = p->data;
                    if (FD_ISSET(sockc, &set1))
                    {
                        printf("创建线程\n");
                        // 创建线程，进行功能
                        pthread_t threadid;                                                        // 设置一个线程id的变量
                        int errnum = pthread_create(&threadid, NULL, fun_pthread, (void *)&sockc); // 创建化线程,
                        // 一个客户端连接上就，就通过一个线程来执行后续操作
                        if (errnum != 0)
                        {
                            printf("%s\n", strerror(errnum));
                        }
                        FD_CLR(sockc, &set2);
                        p = p->next;
                        list_delete_(&head, sockc);
                        continue;
                    }
                    p = p->next;
                }
            }
        }
    }
    list_free_(&head);
}

void test_1(char *str1, char *str2)
{
    printf("服务器开启....\n");
    int sockfd_1, sockfd_2;

    sockfd_1 = sock_init(str1, str2);
    sock__ = sockfd_1; // 将监听套接字保存，以便关闭监听套接字
    my_select(sockfd_1);

    close(sockfd_1); // 7.关闭套接字
    close(sockfd_2);
}